package com.itheima.controller;


import com.itheima.entity.MyLock;
import com.itheima.mapper.MyLockMapper;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@SuppressWarnings("ALL")
@RestController
public class StockController {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private MyLockMapper myLockMapper;
    @Autowired
    private CuratorFramework curatorFramework;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 未加锁
     * @return
     */
    @GetMapping("/stock")
    public String stock() {
        int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
        if (stock > 0) {
            stock--;
            redisTemplate.opsForValue().set("stock", stock + "");
            System.out.println("库存扣减成功，剩余库存：" + stock);
        } else {
            System.out.println("库存不足！！！");
        }
        return "OK";
    }

    /**
     * synchronized
     * @return
     */
    @GetMapping("/stock1")
    public String stock1() {
        synchronized (this) {
            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                stock--;
                redisTemplate.opsForValue().set("stock", stock + "");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            } else {
                System.out.println("库存不足！！！");
            }
            return "OK";
        }
    }

    /**
     * mysql分布式锁
     * @return
     */
    @GetMapping("/stock2")
    public String stock2() {
        MyLock entity = new MyLock();
        entity.setMethodName("stock");

        try {
            //插入数据，如果不抛异常则表示插入成功，即获得锁
            myLockMapper.insert(entity);

            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                stock --;
                redisTemplate.opsForValue().set("stock",stock+"");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            }else {
                System.out.println("库存不足！！！");
            }

            //释放锁
            myLockMapper.deleteByMethodName("stock");
        }catch (Exception ex){
            System.out.println("没有获取锁，不能执行减库存操作！！！");
        }

        return "OK";
    }

    /**
     * zookeeper分布式锁
     * @return
     */
    @GetMapping("/stock3")
    public String stock3() {
        InterProcessMutex mutex = new InterProcessMutex(curatorFramework,"/mylock");

        try {
            //尝试获得锁
            boolean locked = mutex.acquire(0, TimeUnit.SECONDS);
            if(locked){
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if(stock > 0){
                    stock --;
                    redisTemplate.opsForValue().set("stock",stock+"");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                }else {
                    System.out.println("库存不足！！！");
                }
                //释放锁
                mutex.release();
            }else{
                System.out.println("没有获取锁，不能执行减库存操作！！！");
            }
        }catch (Exception ex){
            System.out.println("出现异常！！！");
        }

        return "OK";
    }

    /**
     * redis分布式锁 set nx
     * @return
     */
    @GetMapping("/stock4")
    public String stock4() {
        try {
            //尝试加锁
            Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock", "mylock");
            if(locked){//加锁成功
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if(stock > 0){
                    stock --;
                    redisTemplate.opsForValue().set("stock",stock+"");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                }else {
                    System.out.println("库存不足！！！");
                }
                //释放锁
                redisTemplate.delete("mylock");
            }else{
                System.out.println("没有获取锁，不能执行减库存操作！！！");
            }
        }catch (Exception ex){
            System.out.println("出现异常！！！");
        }

        return "OK";
    }

    /**
     * redis分布式锁 set nx exprie
     * 当某个线程获得锁后程序挂掉，此时还没来得及释放锁，这样后面所有的线程都无法获得锁了。为了解决这个问题可以在加锁时设置一个过期时间防止死锁
     * @return
     */
    @GetMapping("/stock5")
    public String stock5() {
        try {
            //尝试加锁
            Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock", "mylock",5000,TimeUnit.MILLISECONDS);
            if(locked){//加锁成功
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if(stock > 0){
                    stock --;
                    redisTemplate.opsForValue().set("stock",stock+"");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                }else {
                    System.out.println("库存不足！！！");
                }
                //释放锁
                redisTemplate.delete("mylock");
            }else{
                System.out.println("没有获取锁，不能执行减库存操作！！！");
            }
        }catch (Exception ex){
            System.out.println("出现异常！！！");
        }

        return "OK";
    }

    /**
     * redis分布式锁 set nx exprie threadId
     * 加锁和解锁必须是同一个客户端，所以在加锁时可以设置当前线程id，在释放锁时判断是否为当前线程加的锁，如果是再释放锁
     * @return
     */
    @GetMapping("/stock6")
    public String stock6() {
        try {
            String threadId = Thread.currentThread().getId()+"";
            //尝试加锁
            Boolean locked = redisTemplate.opsForValue().setIfAbsent("mylock",threadId,5000,TimeUnit.MILLISECONDS);
            if(locked){//加锁成功
                int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
                if(stock > 0){
                    stock --;
                    redisTemplate.opsForValue().set("stock",stock+"");
                    System.out.println("库存扣减成功，剩余库存：" + stock);
                }else {
                    System.out.println("库存不足！！！");
                }
                String myValue = redisTemplate.opsForValue().get("mylock");
                if(threadId.equals(myValue)){
                    //释放锁
                    redisTemplate.delete("mylock");
                }
            }else{
                System.out.println("没有获取锁，不能执行减库存操作！！！");
            }
        }catch (Exception ex){
            System.out.println("出现异常！！！");
        }

        return "OK";
    }

    @GetMapping("/stock7")
    public String stock7() {
        //获得分布式锁对象，注意，此时还没有加锁成功
        RLock lock = redissonClient.getLock("mylock");
        try {
            //尝试加锁，如果加锁成功则后续程序继续执行，如果加锁不成功则阻塞等待
            lock.lock(5000,TimeUnit.MILLISECONDS);

            int stock = Integer.parseInt(redisTemplate.opsForValue().get("stock"));
            if(stock > 0){
                stock --;
                redisTemplate.opsForValue().set("stock",stock+"");
                System.out.println("库存扣减成功，剩余库存：" + stock);
            }else {
                System.out.println("库存不足！！！");
            }
        }catch (Exception ex){
            System.out.println("出现异常！！！");
        }finally {
            //解锁
            lock.unlock();
        }

        return "OK";
    }
}